/**
 * 
 */
package optimization.naturalOptimization.particleSwarmOptimization;

import java.util.ArrayList;

import multithreading.IndividualStepper;
import optimization.naturalOptimization.NaturalOptimization;
import optimization.naturalOptimization.particleSwarmOptimization.neighborhood.CircleNeighborhood;
import optimization.naturalOptimization.particleSwarmOptimization.neighborhood.GlobalNeighborhood;
import optimization.naturalOptimization.particleSwarmOptimization.neighborhood.PSONeighborhood;
import optimization.naturalOptimization.particleSwarmOptimization.neighborhood.StarNeighborhood;
import optimization.naturalOptimization.particleSwarmOptimization.neighborhood.VonNeumannNeighborhood;
import optimization.naturalOptimization.population.Population;
import optimization.naturalOptimization.population.individual.pso.Particle;

/**
 * This class provides the superclass for the particle swarm algorithms.
 * 
 * @author Kevin Wagner
 * @version 1.0
 */
public abstract class ParticleSwarmOptimization extends NaturalOptimization {

	private Population swarm = new Population(this);
	private PSONeighborhood neighborhood = new GlobalNeighborhood(this);

	/**
	 * Returns the swarm size.
	 * 
	 * @return the swarmSize
	 */
	public int getSwarmSize() {
		return Integer.valueOf(getProperties().getProperty("swarmSize", "30"));
	}

	/**
	 * Returns the swarm.
	 * 
	 * @return the swarm
	 */
	public Population getSwarm() {
		return swarm;
	}

	/**
	 * Returns the neighborhood (topology) for the pso algorithm.
	 * 
	 * @return the neighborhood
	 */
	public PSONeighborhood getNeighborhood() {
		return neighborhood;
	}

	/**
	 * Sets the neighborhood (topology) for the pso algorithm.
	 * 
	 */
	private void setNeighborhood() {
		String temp = getProperties().getProperty("psoNeighborhood", "global");
		if (temp.toLowerCase().equals("global")) {
			this.neighborhood = new GlobalNeighborhood(this);
		} else if (temp.toLowerCase().equals("circle")) {
			this.neighborhood = new CircleNeighborhood(this);
		} else if (temp.toLowerCase().equals("star")) {
			this.neighborhood = new StarNeighborhood(this);
		} else if (temp.toLowerCase().equals("vonneumann")) {
			this.neighborhood = new VonNeumannNeighborhood(this);
		} else {
			this.neighborhood = new GlobalNeighborhood(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.naturalOptimization.NaturalOptimization#getParameters()
	 */
	public ArrayList<String> getParameters() {
		ArrayList<String> parameters = super.getParameters();
		parameters.add("Swarm Size");
		parameters.add("Neighborhood (Topology)");
		parameters.add("Vmax");
		parameters.add("C1 (cognitive component)");
		parameters.add("C2 (social component)");
		return parameters;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.naturalOptimization.NaturalOptimization#
	 * getParameterConfiguration()
	 */
	public ArrayList<String> getParameterConfiguration() {
		ArrayList<String> configuration = super.getParameterConfiguration();
		configuration.add(getSwarmSize() + "");
		configuration.add(getNeighborhood().getIndentification());
		configuration.add(getVMax()+"");
		configuration.add(getC1() + "");
		configuration.add(getC2() + "");
		return configuration;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.naturalOptimization.NaturalOptimization#performStep()
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public boolean performStep() {
		IndividualStepper[] steppers = new IndividualStepper[getSwarmSize()];
		for (int i = 0; i < getSwarmSize(); i++) {
			steppers[i] = new IndividualStepper((Particle) swarm
					.getAllIndividuals().get(i));
			steppers[i].start();
		}
		for (int i = 0; i < getSwarmSize(); i++) {
			try {
				steppers[i].join();
			} catch (InterruptedException e) {
				pushError(e);
			}
		}
		try {
			getFitness().getFitness(swarm);
		} catch (Exception e) {
			pushError(e);
		}
		for (int i = 0; i < getSwarmSize(); i++) {
			Particle p = (Particle) swarm.getIndividual(i);
			if (getFitness().compare(p.getPositionFitness(),
					p.getPersonalBestFitness()) == 1) {
				p.setPersonalBest(p.getPosition());
				p.setPersonalBestFitness(p.getPositionFitness());
			}
		}
		getNeighborhood().setNeighborhoodbest();
		setResult(getCurrentResults());
		return true;
	}

	@SuppressWarnings("rawtypes")
	private double[] getCurrentResults() {
		double[] results = new double[4];
		double best = ((Particle) swarm.getAllIndividuals().get(0))
				.getPositionFitness();
		double worst = best;
		double mean = best;
		double std = 0.0;
		for (int i = 1; i < swarm.getNumberOfIndividuals(); i++) {
			Particle current = (Particle) swarm.getAllIndividuals().get(i);
			if (getFitness().compare(best, current.getPositionFitness()) == 2) {
				best = current.getPositionFitness();
			}
			if (getFitness().compare(worst, current.getPositionFitness()) == 1) {
				worst = current.getPositionFitness();
			}
			mean += current.getPositionFitness();
		}
		mean /= swarm.getNumberOfIndividuals();
		for (int i = 1; i < swarm.getNumberOfIndividuals(); i++) {
			Particle current = (Particle) swarm.getAllIndividuals().get(i);
			std += Math.pow((current.getPositionFitness() - mean), 2);
		}
		std /= (swarm.getNumberOfIndividuals() - 1);
		std=Math.sqrt(std);
		results[0] = best;
		results[1] = worst;
		results[2] = mean;
		results[3] = std;
		return results;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.OptimizationI#init()
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public boolean init() {
		setNeighborhood();
		super.init();
		for (int i = 0; i < getSwarmSize(); i++) {
			Particle p = getNewParticle();
			p.setPosition(getSearchSpace().createRandomPosition());
			p.setPersonalBest(p.getPosition());
			p.setVelocity(getSearchSpace().createVelocity(getVMax()));
			getSwarm().addIndividual(p);
		}
		try {
			getFitness().getFitness(swarm);
		} catch (Exception e) {
			pushError(e);
		}
		for (int i = 0; i < getSwarmSize(); i++) {
			Particle p = (Particle) swarm.getAllIndividuals().get(i);
			p.setPersonalBestFitness(p.getPositionFitness());
		}
		getNeighborhood().setNeighborhoodbest();
		setResult(getCurrentResults());
		exportIndividualData();
		setStatus(NaturalOptimization.inizialized);
		return true;
	}

	/**
	 * Returns the acceleration constant c<sub>1</sub> for the pso algorithm.
	 * 
	 * @return the c<sub>1</sub>
	 */
	public double getC1() {
		return Double.valueOf(getProperties().getProperty("C1", "1.0"));
	}

	/**
	 * Returns the acceleration constant c<sub>2</sub> for the pso algorithm.
	 * 
	 * @return the c<sub>2</sub>
	 */
	public double getC2() {
		return Double.valueOf(getProperties().getProperty("C2", "1.0"));
	}
	
	/**
	 * Returns the acceleration constant c<sub>2</sub> for the pso algorithm.
	 * 
	 * @return the c<sub>2</sub>
	 */
	public double getVMax() {
		return Double.valueOf(getProperties().getProperty("vmax","50.0"));
	}


	/**
	 * Returns a new Particle for the optimization.
	 * 
	 * @return new Particle
	 */
	@SuppressWarnings({ "rawtypes" })
	public abstract Particle getNewParticle();

}
